import { CheckUtil } from './check';
import { DateUtil } from './date';

/** 对象相关包 **/
export const ObjectUtil = {
  /**
   * 深拷贝
   * @param target
   * @param hash
   * @returns {HTMLElement|any|RegExp|Date}
   */
  deepClone(target, hash = new WeakMap()) {
    if (target === null) {
      return target;
    } // 如果是 null 就不进行拷贝操作
    if (target instanceof Date) {
      return new Date(target);
    } // 处理日期
    if (target instanceof RegExp) {
      return new RegExp(target);
    } // 处理正则
    if (target instanceof HTMLElement) {
      return target;
    } // 处理 DOM元素

    if (typeof target !== 'object') {
      return target;
    } // 处理原始类型和函数 不需要深拷贝，直接返回

    // 是引用类型的话就要进行深拷贝
    if (hash.get(target)) {
      return hash.get(target);
    } // 当需要拷贝当前对象时，先去存储空间中找，如果有的话直接返回
    const cloneTarget = new target.constructor(); // 创建一个新的克隆对象或克隆数组
    hash.set(target, cloneTarget); // 如果存储空间中没有就存进 hash 里

    Object.keys(target).forEach((key) => {
      cloneTarget[key] = this.deepClone(target[key], hash); // 递归拷贝每一层
    });

    return cloneTarget; // 返回克隆的对象
  },

  /**
   * 深度合并
   * @param obj
   * @param source
   * @param args
   */
  assign(obj, source, ...args) {
    if (!CheckUtil.isPlainObject(obj) || !CheckUtil.isPlainObject(source)) {
      return;
    }

    Object.assign(obj, this.deepClone(source));

    if (CheckUtil.isNotEmptyArray(args)) {
      this.assign(obj, this.deepClone(args[0]), ...args.slice(1));
    }
  },

  /**
   * 将对象上存在的某个属性名替换成另一个属性名
   * @param obj
   * @param formProp
   * @param toProp
   * @returns {}
   */
  changeProp(obj, formProp, toProp) {
    if (!CheckUtil.isPlainObject(obj)) {
      return;
    }

    if (this.hasOwnProp(obj, formProp)) {
      const val = JSON.parse(JSON.stringify(obj[formProp]));
      delete obj[formProp];
      obj[toProp] = val;
    }
  },

  /**
   * 判断对象是否存在，且检测对象是否含有某个属性
   * @param obj
   * @param prop
   * @returns {boolean|boolean}
   */
  hasOwnProp(obj, prop) {
    return CheckUtil.isPlainObject(obj) && Object.prototype.hasOwnProperty.call(obj, prop);
  },

  /**
   * 如果 obj2 中存在 obj1 相对应的属性，将其值覆盖到 obj1 中（不会新增 obj1 的属性）
   * ({name: "n"}, {name: "name", age: "childAge"}) => {name: "name"}
   * @param obj1
   * @param obj2
   */
  mergePathObj(obj1, obj2) {
    if (!CheckUtil.isPlainObject(obj1) || !CheckUtil.isPlainObject(obj2)) {
      return;
    }

    Object.keys(obj1).forEach((key) => {
      if (this.hasOwnProp(obj2, key)) {
        obj1[key] = obj2[key];
      }
    });

    return obj1;
  },

  /**
   * 将 obj2 中部分属性添加或覆盖到 obj1 中（会新增 obj1 的属性）
   * ({}, {name: "n", age: 23}, {name: "name", age: "childAge"}) => {name: "n", age: "childAge"}
   * ({}, {name: "n", age: 23}, ["name"]) => {name: "n"}
   * @param obj1
   * @param obj2
   * @param fields
   */
  mergeObj(obj1, obj2, fields) {
    if (!CheckUtil.isPlainObject(obj1) || !CheckUtil.isPlainObject(obj2) || (!CheckUtil.isPlainObject(fields) && !CheckUtil.isArray(fields))) {
      return;
    }

    if (CheckUtil.isPlainObject(fields)) {
      Object.keys(fields).forEach((key) => {
        if (Object.hasOwnProperty.call(obj2, key)) {
          obj1[fields[key]] = obj2[key];
        }
      });
    } else {
      fields.forEach((key) => {
        if (Object.hasOwnProperty.call(obj2, key)) {
          obj1[key] = obj2[key];
        }
      });
    }
  },

  /**
   * 对象初始化，属性值：
   *    对象 => 再次初始化
   *    数组 => []
   *    其他 => null
   * @param obj
   * @returns obj <初始化之后的对象>
   */
  initObj(obj) {
    if (!CheckUtil.isPlainObject(obj)) {
      return obj;
    }

    Object.keys(obj).forEach((key) => {
      if (CheckUtil.isPlainObject(obj[key])) {
        this.initObj(obj[key]);
      } else if (CheckUtil.isArray(obj[key])) {
        obj[key] = [];
      } else {
        obj[key] = null;
      }
    });
  },

  /**
   * 根据数组元素过滤对象,返回数组元素为属性组成的对象
   * @param obj
   * @param arr
   * @returns {{}} <一个新对象>
   */
  filterObj(obj, arr) {
    if (!CheckUtil.isPlainObject(obj) || !CheckUtil.isSimpleArray(arr)) {
      return {};
    }

    const res = {};
    Object.keys(obj).forEach((key) => {
      if (arr.includes(key)) {
        res[key] = obj[key];
      }
    });

    return res;
  },

  /**
   * 清洗数据对象
   *    去掉值为 undefined 或 null 的键值对，
   *    去掉以"_","$"开头的属性，
   *    时间格式化
   * @param obj
   * @param cleanFieldList <需要去除的字段>
   * @param cleanPrefixList <需求去除以某字符开头的字段>
   * @returns {{}} <一个新对象>
   */
  cleanObj(obj, cleanFieldList = ['createDate', 'modifyDate', 'makeBillMan', 'modifier', 'deleteFlag'], cleanPrefixList = ['_', '$']) {
    if (!CheckUtil.isPlainObject(obj)) {
      return {};
    }

    const res = {};
    Object.keys(obj).forEach((key) => {
      if (!cleanPrefixList.includes(key[0])) {
        let val = obj[key];
        if (CheckUtil.isPlainObject(val)) {
          res[key] = this.cleanObj(val, cleanFieldList, cleanPrefixList);
        } else if (CheckUtil.isArray(val)) {
          res[key] = this.cleanArr(val, cleanFieldList, cleanPrefixList);
        } else if (CheckUtil.isDef(val)) {
          if (!cleanFieldList.includes(key)) {
            if (CheckUtil.isDate(val)) {
              val = DateUtil.getFormat(val);
            }

            res[key] = val;
          }
        }
      }
    });
    return res;
  },

  /**
   * 清洗数据数组
   *    去掉值为 undefined 或 null 的键值对，
   *    去掉以"_","$"开头的属性，
   *    时间格式化
   * @param arr
   * @param cleanFieldList <需要去除的字段>
   * @param cleanPrefixList <需求去除以某字符开头的字段>
   * @returns {{}} <一个新对象>
   */
  cleanArr(arr, cleanFieldList = ['createDate', 'modifyDate', 'makeBillMan', 'modifier', 'deleteFlag'], cleanPrefixList = ['_', '$']) {
    if (!CheckUtil.isArray(arr)) {
      return [];
    }

    const res = new Array(arr.length);
    arr.forEach((item, index) => {
      if (CheckUtil.isPlainObject(item)) {
        res[index] = this.cleanObj(item, cleanFieldList, cleanPrefixList);
      } else if (CheckUtil.isArray(item)) {
        res[index] = this.cleanObj(item, cleanFieldList, cleanPrefixList);
      } else if (CheckUtil.isDef(item)) {
        if (!cleanFieldList.includes(item) && !cleanPrefixList.includes(item[0])) {
          if (CheckUtil.isDate(item)) {
            item = DateUtil.getFormat(item);
          }

          res[index] = item;
        }
      }
    });
    return res;
  },

  /**
   * 根据 standardField， 在 options 中找到对应的 insertFields, 并添加到 obj 中
   * @example
   * ```
   *   let data = {cId:1};
   *   insertValToObj(data, [{id:1,code:"one"},{id:2,code:"two"}], {cCode:"code"},{cId:"id"})
   *   console.log(data); // {cId: 1, cCode: "two"}
   * ```
   * @param obj
   * @param options {array}
   * @param insertFields <key1: value1, key2: value2, ...>
   * @param standardField <key: value>
   */
  insertValToObj(obj, options, insertFields, standardField) {
    if (
      !CheckUtil.isPlainObject(obj) ||
      !CheckUtil.isObjectArray(options) ||
      !CheckUtil.isPlainObject(insertFields) ||
      !CheckUtil.isPlainObject(standardField)
    ) {
      return;
    }

    const field = Object.keys(standardField)[0];
    const item = options.find((o) => o[standardField[field]] === obj[field]);
    if (CheckUtil.isNotEmptyObject(item)) {
      Object.keys(insertFields).forEach((key) => {
        obj[key] = item[insertFields[key]];
      });
    }
  },
  // 数组比对
  areObjectsEqual(obj1, obj2) {
    // 如果两个对象是同一个引用，直接返回 true
    if (obj1 === obj2) return true;

    // 如果其中一个对象是 null 或 undefined，直接返回 false
    if (!obj1 || !obj2) return false;

    // 获取两个对象的键
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);

    // 如果键的数量不同，直接返回 false
    if (keys1.length !== keys2.length) return false;

    // 遍历第一个对象的键
    for (const key of keys1) {
      // 如果第二个对象中没有该键，或者两个对象的值不相等，返回 false
      if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
        return false;
      }
    }

    // 如果所有键和值都匹配，返回 true
    return true;
  }
};
